RustでのAWS Lambda開発に必要なツールを揃えてみた
はじめに
最近RustでちょっとしたAWS Lambdaの関数を書く機会があって開発に必要なものを色々試したので紹介します。
RustでのLambda 関数の開発に必要そうなもの
今回は以下のツールや設定を準備しました。この辺りはだいたいどんなケースでも必要になるのではないかと思います。
- カスタムランタイム ⇒ aws-lambda-rust-runtime
- 環境変数で渡すパラメータの設定 ⇒ direnv
- クロスコンパイル環境 ⇒ softprops/lambda-rust
- AWS APIクライアント ⇒ rusoto
- AWS リソースのスタブ ⇒ localstack
- ローカル実行 ⇒ lambci/lambda
- デプロイ ⇒ awscli
これらの設定を順番にみていきます。また今回の設定はGitHubで公開しています。
カスタムランタイム
素直にaws-lambda-rust-runtimeを使っています。開発が活発になってきて嬉しいです。
クロスコンパイル環境
私はmacOS上で開発しているのでAWS上にデプロイするにはクロスコンパイルを行う必要があります。muslのビルド環境を整えるのに挫折したのでsoftprops/lambda-rust を使わせてもらいます。開発中は下記のようなcargo makeのタスクを使ってビルドしています。
ポイントは
- READMEにある通りcargoのキャッシュを設定する
- 開発中のビルドタスクではdevプロファイルを指定する
Makefile.toml
[env] LAMBDA_RUST_TAG = "0.3.0-rust-1.45.0" [tasks.build-debug] description = "Build for debug" script = [''' docker run --rm \ -v ${PWD}:/code \ -v ${PWD}/.cache/cargo/registry:/root/.cargo/registry \ -v ${PWD}/.cache/cargo/git:/root/.cargo/git \ -e PROFILE=dev \ softprops/lambda-rust:${LAMBDA_RUST_TAG} ''' ]
環境変数の設定 direnv
各自の開発環境の設定はdirenvで行います。AWS上にデプロイした時に環境ごとの設定を環境変数で行うことも多いと思います。direnvはenvrcではなくdotenv形式で設定するようにします。dotenv形式で設定を記述することでdockerの実行時にも設定を流用できます。
今回は以下のように設定しました。
envrc
dotenv
.env
# localstackのホスト側ポート番号 # 各自の環境で使いたいポートが違うので環境変数で設定できるようにする(後述) LOCAL_STACK_PORT=4567 # ダミーのAWSクレデンシャル # localstackを使う場合でも何かしらクレデンシャルは必要なので # AWSデプロイ時には関数に設定したロールが使われる AWS_ACCESS_KEY_ID=XXXXXX AWS_SECRET_ACCESS_KEY=YYYYYY # 実行時にrusotoの設定をオーバーライドするための設定(後述) override_endpoint=http://localhost:${LOCAL_STACK_PORT}
AWS APIクライアント
AWSのクライアントにはrusotoを使います。以下のような設定を作って開発環境ではlocalstackを向くようにします。設定自体はconfig crate で環境変数からロードします。
#[derive(Deserialize, Debug)] pub struct AwsConfig { override_endpoint: Option<String>, } impl AwsConfig { pub fn to_region(&self) -> Region { self.override_endpoint.clone().map_or_else( || Default::default(), |e| Region::Custom { name: "us-east-1".to_string(), endpoint: e.to_string(), }, ) } }
AWSリソースのスタブ localstack
AWSリソースのスタブには使い慣れているlocalstackを使いました。docker-compose.ymlはこんな感じです。ポイントは下記の通りです。
- ホスト側のポートを各自好きに設定できるように環境変数を使う
- リソースの作成を行うスクリプトを
/docker-entry-initaws.d
にマッピングする
docker-compose.yml
version: "3.9" services: localstack: image: localstack/localstack:0.12.8 ports: - "${LOCAL_STACK_PORT}:4566" environment: - SERVICES=dynamodb volumes: - ./localstack/init_scripts:/docker-entrypoint-initaws.d
ローカル実行 lambci/lambda
こちらもMakefile.tomlに実行用のタスクを定義しておきます。先ほどのビルドタスクを依存関係に入れてその成果物を使います。
ポイントは下記の通りです。
- テスト用のイベントを標準入力から渡す(JSONファイルやスクリプトで自由に生成できる)
- 環境変数を.envファイルから参照する
—network
オプションでコンテナはlocalstackと同じネットワークで起動する- ※docker-composeのデフォルトのネットワーク名は
<プロジェクト名>-default
- ※docker-composeのデフォルトのネットワーク名は
- localstackと同じネットワークなので環境変数
override_endpoint
にはサービス名を使うようにする
[tasks.run-local] description = "run lambda function locally" script = [ ''' unzip -o \ target/lambda/debug/${CARGO_MAKE_CRATE_NAME}.zip \ -d /tmp/lambda ''', ''' cat event.json | \ docker run \ -i \ --env-file=.env \ -e DOCKER_LAMBDA_USE_STDIN=1 \ -e override_endpoint=http://localstack:${LOCAL_STACK_PORT} \ --rm \ -v /tmp/lambda:/var/task \ --network ${CARGO_MAKE_CRATE_NAME}_default \ lambci/lambda:provided ''' ] dependencies = ['build-debug']
デプロイ
aws-lambda-rust-runtimeのREADMEにawscliでのデプロイ例があります。
serverless-rustプラグイン
ここまで触れてこなかったですが、AWS環境に作成するリソースや環境ごとの環境変数の設定を管理するにはserverless frameworkのserverless-rustプラグインを使った方が楽だと思います。
その場合はプラグインがデフォルトで使うビルド用イメージのRustツールチェインが古いのでタグを明示すると新しめのイメージを使えます。
custom: # ツールチェインのバージョンを指定するためにタグを上書き rust: dockerTag: '0.3.0-rust-1.45.0' dockerImage: 'softprops/lambda-rust'
まとめ
RustでAWS Lambda関数を開発する時に必要なツールを自分なりにまとめてみました。複数のツールを組み合わせることでストレスなく開発できる環境がセットアップできたと思います。コンテナ上でのビルドが少し遅いのでその点も改善できたらいいなと思います。